import * as React from 'react';
import { Upload, message, notification } from 'antd';

export interface Props {
    /**
     * 是否将文件转成base64上传
     */
    isToBase64?: boolean;
    /**
     * 同原组件Upload中的fileList.用于展示上传的文件
     */
    fileList: Array<any>;
    /**
     * 系统代号.如果商家为"Merchant".必传,后端接口需要.
     */
    sysCode: string;
    /**
     * 业务归属编号.如门店为 "Branch".目前可不传，后端接口需要.
     */
    businessCode: string;
    /**
     * 业务类目.如门店头图为 "BranchHead".
     * 营业资质中有两个分别为"acraFile" 及 "acraImage"
     * 必传,后端接口需要.
     */
    categoryCode: string;
    /**
     * 上传限定的文件格式,多种文件格式用","隔开.
     * 例如"PNG,JPG",如不需要限制,可不传该参数.
     */
    checkFileType: string;
    /**
     * 上传限定的文件格式不符合提示信息
     *  例如"只能上传PNG,JPG".
     */
    checkFileTypeMsg: string;
    /**
     * 上传限定的文件大小,单位为MB.
     * 例:checkFileSize={3},表示上传文件大小不能超过3m.
     */
    checkFileSize: number;
    /**
     * 上传的文件大小不符合提示信息
     *  例如"文件大小不能超过 3MB".
     */
    checkFileSizeMsg: string;
    /**
     * uploadUrl:上传url接口,目前统一接口路径为 "/api/resource/producePutPreSignUrls"
     */
    uploadUrl: string;
    /**
     * 是否拖拽上传
     */
    dragAble: boolean;

    /**
     * 是否 单图 默认false
     */
    isSingle: boolean;

    /**
     * 是否  需要 等待上传反馈 默认等待
     */
    isWait: boolean;

    /**
     * 需要上传的资源文件,对象结构为
     * {categoryCode:'',keyNameAndOpt:[]}
     * 初始化可为null，对象通过setFileList返回。
     */
    uploadResource?: {
        categoryCode?: string;
        keyNameAndOpt?: Array<string>;
    } | null;
    /**
     * 回调函数,返回业务页面需要的保存跟展示的数据.
     * 返回参数为: showFileList及 uploadResource,
     * 分别为修改后的展示的文件列表及需要上传的资源文件.
     */
    setFileList: (Array, Object) => void;
}

const codeMessage: { [key: number]: string } = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队（异步任务）。',
    204: '删除数据成功。',
    400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限（令牌、用户名、密码错误）。',
    403: '用户得到授权，但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除，且不会再得到的。',
    422: '当创建一个对象时，发生一个验证错误。',
    500: '服务器发生错误，请检查服务器。',
    502: '网关错误。',
    503: '服务不可用，服务器暂时过载或维护。',
    504: '网关超时。'
};

const checkStatus = (response: Response) => {
    if (response.status >= 200 && response.status < 300) {
        return response;
    }
    const errortext = codeMessage[response.status] || response.statusText;
    notification.error({
        message: `请求错误 ${response.status}: ${response.url}`,
        description: errortext
    });
    const error: any = new Error(errortext);
    error.name = response.status;
    error.response = response;
    throw error;
};

/**
 * 原生ajax上传，用以显示上传进度
 * @param url 上传路径
 * @param opts 上传配置项
 * @param progressOptions 上传文件的进度配置
 */
const fetch = (url: string, opts: any = {}, progressOptions: any = null) =>
    /* eslint-disable compat/compat */
    new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(opts.method || 'GET', url);
        Object.keys(opts.headers || {}).forEach(key => {
            xhr.setRequestHeader(key, opts.headers[key]);
        });
        xhr.onload = (e: any) => {
            const {
                target: { responseText, status }
            } = e;
            resolve({
                text: () => responseText,
                json: () => JSON.parse(responseText) || {},
                status
            });
        };
        xhr.onerror = reject;
        if (xhr.upload && progressOptions) {
            const { file, onProgress } = progressOptions;
            xhr.upload.onprogress = e => {
                const { loaded, total } = e;
                onProgress({ percent: Math.round((loaded / total) * 100) }, file);
            };
        }
        xhr.send(opts.body);
    });
function request(url: string, option: any) {
    const defaultOptions = {
        credentials: 'include'
    };
    const newOptions = { ...defaultOptions, ...option };
    if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'DELETE') {
        if (!(newOptions.body instanceof FormData)) {
            newOptions.headers = {
                Accept: 'application/json',
                'Content-Type': 'application/json; charset=utf-8',
                ...newOptions.headers
            };
            newOptions.body = JSON.stringify(newOptions.body);
        } else {
            newOptions.headers = {
                Accept: 'application/json',
                ...newOptions.headers
            };
        }
    }
    return fetch(url, newOptions)
        .then(checkStatus)
        .then((response: Response) => {
            if (newOptions.method === 'DELETE' || response.status === 204) {
                return response.text();
            }
            return response.json();
        });
}

// 上传至服务器
function uploadToServer(params: { preUrl: string; file: File; onProgress: any }) {
    // headers:{'Content-Type':'application/octet-stream'} 很重要！很重要！很重要！
    const defaultOptions: any = {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/octet-stream'
        }
    };
    console.log(params.file, 'params.fileparams.fileparams.fileparams.file')
    defaultOptions.body = params.file;

    // 上传进度
    const progressOptions = {
        file: params.file,
        onProgress: params.onProgress
    };

    return fetch(params.preUrl, defaultOptions, progressOptions)
        .then(checkStatus)
        .then(response => response)
        .catch(e => e);
}

class UploadOss extends React.PureComponent<Props, any> {
    keyMap = {};

    uploadResource = null;

    static defaultProps = {
        fileList: [],
        categoryCode: '',
        checkFileType: '',
        checkFileSize: 0,
        checkFileLength: 0,
        sysCode: '',
        businessCode: '',
        uploadUrl: '',
        dragAble: false,
        isSingle: false,
        isWait: true,
        checkFileTypeMsg: null,
        checkFileSizeMsg: null,
        checkFileLengthMsg: null,
        setFileList: () => {}
    };

    // 校验上传文件类型
    checkUpload = (file: File, fileList: Array<File>) => {
        const { checkFileType = '', checkFileSize = 0, checkFileTypeMsg = null, checkFileSizeMsg = null } = this.props;

        if (checkFileType.length > 0) {
            const fileType = file.name.substring(file.name.lastIndexOf('.') + 1);
            if (!checkFileType.toLocaleLowerCase().includes(fileType.toLocaleLowerCase())) {
                if (checkFileTypeMsg) {
                    message.error(checkFileTypeMsg);
                } else {
                    message.error(`You can only upload ${checkFileType}`);
                }

                fileList.splice(fileList.indexOf(file), 1);
                return false;
            }
        }
        if (checkFileSize > 0) {
            const isLess = file.size / 1024 / 1024 < checkFileSize;
            if (!isLess) {
                if (checkFileSizeMsg) {
                    message.error(checkFileSizeMsg);
                } else {
                    message.error(`File must smaller than ${checkFileSize}MB!`);
                }

                fileList.splice(fileList.indexOf(file), 1);
                return false;
            }
        }
        return true;
    };

    // 上传
    customUpload = ({ file, onError, onSuccess, onProgress }) => {
        const { sysCode, businessCode, uploadUrl } = this.props;
        const { keyMap } = this;
        const arr = [];
        arr.push(file.name)
        const params = {
            sysCode,
            businessCode,
            fileNames: arr,
            file
        };

        const uploadFn = async () => {
            // 获取预签名路径
            const response = await request(uploadUrl, {
                method: 'POST',
                body: params
            });
            // 返回内容
            if (response.data && response.data.length > 0) {
                const { preUrl, keyName } = response.data[0];
                const uploadParams = { preUrl, file, onProgress };
                keyMap[file.name] = keyName;
                // 上传至服务器
                await uploadToServer(uploadParams);
                onSuccess(response, file);
            } else {
                onError(new Error('sign error'));
            }
        };

        try {
            uploadFn();
        } catch (e) {
            onError(e);
        }
    };

    // 删除文件
    removeUpload = (file: any) => {
        const { fileList, setFileList, categoryCode, uploadResource } = this.props;
        const { keyMap } = this;
        const keyName = file.keyName || keyMap[file.name];
        // 初始化
        const uploadResourceResult = uploadResource || {
            categoryCode,
            keyNameAndOpt: []
        };

        const { keyNameAndOpt } = uploadResourceResult;
        const existIndex = keyNameAndOpt.findIndex(item => item === `ADD:${keyName}`);
        if (existIndex > -1) {
            // 删除新增
            keyNameAndOpt.splice(existIndex, 1);
        } else {
            // 删除已有
            keyNameAndOpt.push(`DELETE:${keyName}`);
        }
        setFileList(fileList, uploadResourceResult);
        return true;
    };

    changeUpload = ({ file, fileList }) => {
        if (!file.status) return;
        const { categoryCode, setFileList, uploadResource, checkFileLength } = this.props;
        const { keyMap } = this;
        if (fileList.length > checkFileLength) {
            message.error('只能上传一张头像');
            return;
        }
        // 把图片转成base64的才不会被裁切
        const toBase64 = (params: any, uploadResourceResult: any) => {
            if (typeof FileReader === 'undefined') {
                message.warning('当前浏览器不支持FileReader');
            } else {
                try {
                    const { originFileObj } = params[0];
                    if (!/image\/\w+/.test(originFileObj.type)) {
                        message.warning('请确保文件为图像类型');
                    } else {
                        const reader = new FileReader();
                        reader.onload = (res: { [propName: string]: any }) => {
                            const {
                                target: { result }
                            } = res;
                            const info = params[0].originFileObj;
                            info.fileUrl = result;
                            info.keyName = `ADD:${keyMap[result]}`;
                            // 缩略图也要更新
                            params[0].thumbUrl = result;
                            // 把转换后的结果返回
                            setFileList([...params], uploadResourceResult);
                        };
                        reader.readAsDataURL(originFileObj);
                    }
                } catch (e) {
                    message.error(`图片转Base64出错啦！${e.toString()}`);
                }
            }
        };
        // before upload也会触发，但是没有status
        const uploadResourceResult = uploadResource || {
            categoryCode,
            keyNameAndOpt: []
        };
        if (file.status === 'done' || file.status === 'removed' || file.status === 'uploading') {
            const { keyNameAndOpt } = uploadResourceResult;
            // 完成
            if (file.status === 'done') {
                keyNameAndOpt.push(`ADD:${keyMap[file.name]}`);
                // 将结果转成没裁切的
                toBase64(fileList, uploadResourceResult);
            }
        }

        setFileList([...fileList], uploadResourceResult);
    };

    render() {
        const { children, dragAble } = this.props;
        const Component = dragAble ? Upload.Dragger : Upload;
        return (
            <Component
                {...this.props}
                customRequest={this.customUpload}
                beforeUpload={this.checkUpload}
                onRemove={this.removeUpload}
                onChange={this.changeUpload}
            >
                {children}
            </Component>
        );
    }
}

export default UploadOss;
